--[[ scheme_type: generic author: Alundaio modified_by: --]] evaid = 188112 actid = 188112 ---------------------------------------------------------------------------------------------------------------------- hostage_list = {} local ini = ini_file("ai_tweaks\\xrs_kill_wounded.ltx") local Enable = ini:r_bool_ex("settings","enable",false) local ActorKillWoundedChance = ini:r_float_ex("settings","actor_cause_surrender_chance") or 0.25 local ActorKillWoundedMaxDist = ini:r_float_ex("settings","actor_cause_surrender_max_dist") or 7 local MinDistanceRunState = ini:r_float_ex("settings","state_run_min_distance") or 8 local MinDistance = ini:r_float_ex("settings","min_distance") or 3 local MaxDistance = ini:r_float_ex("settings","max_distance") or 5 local MinVectorRotateY = ini:r_float_ex("settings","min_vector_rotate_y") or 1 local MaxVectorRotateY = ini:r_float_ex("settings","max_vector_rotate_y") or 95 local MinTriggerTimer = ini:r_float_ex("settings","min_timer","float") or 3000 local MaxTriggerTimer = ini:r_float_ex("settings","max_timer","float") or 5000 local SurrenderMinTriggerTimer = ini:r_float_ex("settings","surrender_min_timer") or 5000 local SurrenderMaxTriggerTimer = ini:r_float_ex("settings","surrender_max_timer") or 5000 local KillWoundedSoundChance = ini:r_float_ex("settings","chance_to_play_sound") or 0.25 local SurrenderChance = ini:r_float_ex("settings","chance_to_play_surrender") or 0.25 local CauseSurrenderCommunities = parse_list(ini,"settings","cause_surrender_communities",true) ini = nil ---------------------------- local level = level local alife = alife ---------------------------- ------------------------------------------ -- Localized Functions ------------------------------------------ local function npc_on_update(npc,st) if not (IsWounded(npc)) then return end --printf("--WOUNDED") if (st.victim_surrender) then --printf("-st.victim_surrender="..st.victim_surrender) else --printf("-st.victim_surrender is not set!") end if not (st.victim_surrender) or (st.victim_surrender ~= 0 and st.victim_surrender ~= 65535) then -- override NPC roll local active_item = db.actor:active_item() if not (active_item and IsWeapon(active_item)) then --printf("-HAS WEAPON") return end if (character_community(npc) == "zombied" or character_community(npc) == "monolith") then --printf("-WRONG COMMUNITY") return end if (npc:relation(db.actor) < game_object.enemy) then --printf("-GOOD RELATION") return end if (npc:position():distance_to(db.actor:position()) > ActorKillWoundedMaxDist) then --printf("-LONG DISTANCE") --if (utils_data.angle_diff(db.actor:direction(), db.actor:position():sub(npc:position())) < 90 or npc:position():distance_to(db.actor:position()) > 7) then return end if ( (math.random(1,100)/100) >= ActorKillWoundedChance) then --printf("-LOW CHANCE") st.victim_surrender = 65535 return end --printf("-SURRENDERED") st.victim_surrender = AC_ID game_statistics.increment_statistic("enemies_surrendered") end end -------------------------------- -- Register Callbacks -------------------------------- function on_game_start() if not (Enable) then return end RegisterScriptCallback("npc_on_update",npc_on_update) end local function get_rand_vertex(npc,vo) local dir = vec_sub(npc:position(),vo:position()) dir = vector_rotate_y(dir,math.random(MinVectorRotateY,MaxVectorRotateY)) return vo:vertex_in_direction(vo:level_vertex_id(),dir,math.random(MinDistance+1,MaxDistance)) end local function is_wounded(npc) if (axr_task_manager.hostages_by_id[npc:id()]) then local squad = get_object_squad(npc) if (squad and squad:commander_id() == npc:id()) then return true end end if (npc:id() ~= 0 and IsStalker(npc) and npc:wounded()) then return true end return false end local function get_hostage(npc) local squad = get_object_squad(npc) if (squad and squad:commander_id() == npc:id()) then for id,v in pairs(axr_task_manager.hostages_by_id) do local vo = db.storage[id] and db.storage[id].object or level.object_by_id(id) if (vo and npc:position():distance_to_sqr(vo:position()) <= 900 and npc:relation(vo) >= game_object.enemy) then return vo end end end end local function validate(npc,vid) if (vid and vid < 4294967295 and npc:accessible(vid) and vid ~= npc:level_vertex_id()) then return db.used_level_vertex_ids[vid] == nil or db.used_level_vertex_ids[vid] == npc:id() end return false end ----------------------------------------------- class "eva_kill_wounded" ( property_evaluator ) function eva_kill_wounded:__init( name, storage ) super ( nil, name ) self.st = storage self.st.force_false = 0 end function eva_kill_wounded:find_target() local npc = self.object local be = npc:best_enemy() or get_hostage(npc) if (be and IsStalker(be) and be:alive() and is_wounded(be)) then if (self.st.selected_id == nil or self.st.selected_id ~= be:id()) then self.st.selected_id = be:id() self.st.vertex_id = nil self.st.vertex_position = nil self.st.timer = nil local bw = npc:active_item() and npc:best_weapon() if (bw and npc:active_item():id() == bw:id()) then npc:set_item(object.idle,bw) end return true end return true end self.st.selected_id = nil self.st.vertex_id = nil self.st.vertex_position = nil self.st.timer = nil return false end function eva_kill_wounded:evaluate() --utils_data.debug_write("eva_kill_wounded") local npc = self.object if not (npc:alive()) then return false end if (is_wounded(npc)) then return false end if (xr_danger.has_danger(npc)) then return false end if not (npc:active_item()) then return false end local st = db.storage[npc:id()] if (st) and (st.active_scheme == "camper" or st.rx_dont_shoot) then -- or st.help_wounded and st.help_wounded.selected_id ~= nil) then return false end return self:find_target() end -------------------------------------------------------------------------------------------------------------- -- Action Kill Wounded class "act_kill_wounded" ( action_base ) function act_kill_wounded:__init( name, storage ) super ( nil, name ) self.st = storage end function act_kill_wounded:initialize() --utils_data.debug_write("act_kill_wounded:initialize") action_base.initialize(self) local npc = self.object local st = self.st st.timer = nil npc:inactualize_patrol_path() npc:set_path_type(game_object.level_path) end function act_kill_wounded:reset() self.st.timer = nil self.st.vertex_id = nil self.st.vertex_position = nil end function act_kill_wounded:execute() --utils_data.debug_write("act_kill_wounded:execute start") action_base.execute(self) local npc = self.object local st = self.st local vo = st.selected_id and db.storage[st.selected_id] and db.storage[st.selected_id].object if not (vo and IsStalker(vo) and vo:alive() and is_wounded(vo)) then st.selected_id = nil return end -- ensure and enforce path type if (npc:path_type() ~= game_object.level_path) then npc:set_path_type(game_object.level_path) end local dist = npc:position():distance_to(vo:position()) if (dist < MinDistance) then self:reset() end local dst = db.storage[npc:id()] if (dst and dst.rx_dont_shoot) then st.timer = nil state_mgr.set_state(npc,"raid",nil,nil,{look_object = vo}) st.vertex_id = utils_obj.send_to_nearest_accessible_vertex(npc,npc:level_vertex_id(),"kill_wounded") return end if not (st.vertex_id) then local vid = get_rand_vertex(npc,vo) if (validate(npc,vid) and level.vertex_position(vid):distance_to_sqr(vo:position()) >= MinDistance) then st.vertex_id = vid st.vertex_position = level.vertex_position(st.vertex_id) end end local dir = vec_sub(utils_obj.safe_bone_pos(vo,"bip01_spine"),utils_obj.safe_bone_pos(npc,"bip01_r_finger02")) local tg = time_global() if (st.timer and tg > st.timer) then if not (axr_task_manager.hostages_by_id[vo:id()]) then if (npc:see(vo)) then state_mgr.set_state(npc,"threat_sniper_fire",nil,nil,{look_object = vo, look_dir = dir}) end end --utils_data.debug_write("act_kill_wounded:execute end 2") return end if (st.vertex_id) then if (npc:level_vertex_id() == st.vertex_id or dist <= MinDistance+1) then state_mgr.set_state(npc,"threat",nil,nil,{look_dir = dir}) if (db.used_level_vertex_ids[st.vertex_id]) then db.used_level_vertex_ids[st.vertex_id] = nil end st.vertex_id = utils_obj.send_to_nearest_accessible_vertex(npc,npc:level_vertex_id(),"kill_wounded") db.used_level_vertex_ids[st.vertex_id] = npc:id() self:begin_kill_wounded(vo) else state_mgr.set_state(npc,"assault",nil,nil,{look_position = vo:position()},{fast_set = true}) if (db.used_level_vertex_ids[st.vertex_id]) then db.used_level_vertex_ids[st.vertex_id] = nil end st.vertex_id = utils_obj.send_to_nearest_accessible_vertex(npc,st.vertex_id,"kill_wounded") db.used_level_vertex_ids[st.vertex_id] = npc:id() end end --utils_data.debug_write("act_kill_wounded:execute end 3") end function act_kill_wounded:begin_kill_wounded(vo) local vst = db.storage[vo:id()] if not (vst) then return end if not (vst.victim_surrender) then if (character_community(vo) ~= "monolith" and character_community(vo) ~= "zombied") then if (CauseSurrenderCommunities[character_community(self.object)] and SurrenderChance > (math.random(1,100)/100) ) then vst.victim_surrender = self.object:id() else vst.victim_surrender = 65534 end end end if (axr_task_manager.hostages_by_id[vo:id()]) then -- force squad enemy to actor because they have a hostage local squad = get_object_squad(self.object) if (squad) then squad:set_squad_relation("enemy") end return -- WE DONT WANT TO KILL THE HOSTAGE FAKE! end if not (self.st.timer) then local tg = time_global() if ((math.random(1,100)/100) < KillWoundedSoundChance) then xr_sound.set_sound_play(self.object:id(),"kill_wounded") end if (vst.victim_surrender) then self.st.timer = tg + math.random(SurrenderMinTriggerTimer,SurrenderMaxTriggerTimer) --printf("%s--%s",vo:name(),vo:get_visual_name()) else self.st.timer = tg + math.random(MinTriggerTimer,MaxTriggerTimer) end if not (self.st.news_triggered) then if (axr_task_manager.hostages_by_id[vo:id()]) then if (dynamic_news_manager and ui_options.get("alife/dynamic_news/death_stalker_news")) then dynamic_news_manager.get_dynamic_news():KillWounded(self.object,vo,true) end else if (dynamic_news_manager and ui_options.get("alife/dynamic_news/death_stalker_news")) then dynamic_news_manager.get_dynamic_news():KillWounded(self.object,vo) end end self.st.news_triggered = true end end end function act_kill_wounded:finalize() action_base.finalize(self) self:reset() local bw = self.object:best_weapon() if (bw and IsWeapon(bw) and (bw:get_state() == 5 or bw:get_state() == 6)) then self.object:set_item(object.idle, bw) end self.object:set_desired_position() self.object:set_desired_direction() end -------------------------------------------------------------------------------------------------------------- ------------------ binder function setup_generic_scheme(npc,ini,scheme,section,stype,temp) local st = xr_logic.assign_storage_and_bind(npc,ini,"kill_wounded",section,temp) end function add_to_binder(npc,ini,scheme,section,st,temp) if not npc then return end local manager = npc:motivation_action_manager() if not manager then return end if (not npc:alive()) or (Enable ~= true or character_community(npc) == "zombied" or npc:section() == "actor_visual_stalker") then manager:add_evaluator(evaid,property_evaluator_const(false)) temp.needs_configured = false return end local evaluator = eva_kill_wounded("eva_kill_wounded",st) temp.action = act_kill_wounded("act_kill_wounded",st) if not evaluator or not temp.action then return end manager:add_evaluator(evaid,evaluator) local wp = world_property temp.action:add_precondition(wp(evaid,true) ) temp.action:add_precondition(wp(stalker_ids.property_alive,true)) temp.action:add_precondition(wp(stalker_ids.property_enemy,true)) temp.action:add_effect(wp(evaid,false)) temp.action:add_effect(wp(stalker_ids.property_enemy,false)) manager:add_action(actid,temp.action) end function configure_actions(npc,ini,scheme,section,stype,temp) if not npc then return end local manager = npc:motivation_action_manager() if not manager or not temp.action then return end local wp = world_property --temp.action:add_precondition(wp(xr_evaluators_id.state_mgr + 2,true)) temp.action:add_precondition(wp(xr_evaluators_id.sidor_wounded_base,false)) temp.action:add_precondition(wp(xr_evaluators_id.wounded_exist,false)) --[[ if (_G.schemes["rx_ff"]) then temp.action:add_precondition(wp(rx_ff.evaid,false)) end --]] local action local p = { --xr_danger.actid, stalker_ids.action_combat_planner, -- stalker_ids.action_danger_planner, xr_actions_id.stohe_meet_base+1, xr_actions_id.state_mgr + 2, xr_actions_id.alife } for i=1,#p do --printf("ACTION_ALIFE_ID(xrs_kill_wounded.configure_actions): " .. tostring(p[i])) action = manager:action(p[i]) if action then action:add_precondition( wp(evaid,false) ) end end end function add_common_precondition(action) if not action then return end action:add_precondition(world_property(evaid,false)) end function is_under_kill_wounded(npc) local mgr = npc:motivation_action_manager() if not mgr or not mgr:initialized() then return false end local current_action_id = mgr:current_action_id() return current_action_id == actid end